// package edu.ua.bmr.claxml;

import java.net.URL;
import java.util.HashMap;
import java.util.Stack;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;


/**
 * Constructs the data set that results from parsing a ClaXML document.
 * For use with SAX.
 * @author pmkilgo
 */
public class ClaXMLHandler extends DefaultHandler {

	/** We will construct this data set */
	private ClaXMLSet mDataSet = new ClaXMLSet();
	
	/** A temporary location to store our collected data */
	private String mCollectedData;
	
	/** We will use a stack to represent the hierarchy of the document primarily for
	 * error checking.
	 */
	private Stack<String> mTagStack = new Stack<String>();

	/**
	 * Retrieves the data set constructed after SAX makes a pass through the XML.
	 * @return the data set for a given XML document.
	 */
	public ClaXMLSet getParsedData() {
		return this.mDataSet;
	}

	/**
	 * Called at the start of a new XML document.
	 */
	@Override
	public void startDocument() throws SAXException {
		mDataSet = new ClaXMLSet();
		mCollectedData = null;
		mTagStack.clear();
	}

	/**
	 * Called at the end of an XML document.
	 */
	@Override
	public void endDocument() throws SAXException {
		if(mTagStack.size() > 0) {
			mDataSet.setInvalid(true);
		}
	}

	/**
	 * Gets be called on opening tags like: <tag> Can provide attribute(s), when
	 * xml was like: <tag attribute="attributeValue">
	 */
	@Override
	public void startElement(String URI, String name, String tagName, Attributes atts) throws SAXException {
		int level = mTagStack.size();
		mTagStack.push(tagName.toLowerCase());
		
		
		switch(level) {
		case 0:
			// We've just entered the top level which should mark
			// the response type.
			mDataSet.setResponseType( tagName );
			break;
			
		case 1:
			// We've entered a second-level which should mark
			// an object type.
			mDataSet.setObjectType(tagName.toLowerCase());
			break;
			
		case 2:
			// This should be an attribute tag.
			// Do nothing for now.
			break;
			
		default:
			// This doesn't make sense.
			mDataSet.setInvalid(true);
			break;
		}
	}

	/**
	 * Gets be called on closing tags like: </tag>
	 */
	@Override
	public void endElement(String URI, String name, String tagName)
			throws SAXException {
		
		if(mTagStack.size() == 0) {
			// This can't be valid if the stack is zero.
			// It would mean we never pushed something on in startElement.
			
			mDataSet.setInvalid(true);
			return;
		}
		
		int level = mTagStack.size();
		
		tagName = tagName.toLowerCase();
		
		// Grab the opening tag.
		String lastTag = mTagStack.pop();
		
		if(lastTag.compareTo(tagName) != 0) {
			// Encapsulating tags don't match up!
			
			mDataSet.setInvalid(true);
			return;
		}
		
		// Tags match up. Now we can see if we need to do anything
		// to the data set.
		if(level == 3) {
			if(mCollectedData != null) {
				HashMap<String,String> map = new HashMap<String,String>();
				map.put(tagName, mCollectedData);
				mDataSet.addObject(map);
				mCollectedData = null;
			}
		}
		
	}

	/**
	 * Gets called on the following structure: <tag>characters</tag>
	 */
	@Override
	public void characters(char ch[], int start, int length) {
		String data = new String(ch, start, length);
		data = data.trim();
		
		if(data.length() > 0) {
			// If there's anything but whitespace, we should have some good stuff!
			mCollectedData = data;
		}
	}
	
	/**
	 * Give it a whirl if you want to test it out.
	 * @param args, where args[0] is the URL you want to test.
	 */
	public static void main(String [] args) {
		if(args.length != 1) {
			System.out.println("Usage: java ClaXMLHandler [URL]");
			return;
		}
		
		ClaXMLHandler h = new ClaXMLHandler();
		SAXParserFactory factory = SAXParserFactory.newInstance();
		String arg = args[0];
		
		SAXParser saxParser;
		try {
			saxParser = factory.newSAXParser();
			URL url = new URL(arg);
			saxParser.parse(url.openStream(), h);
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println( h.getParsedData() );
	}
}
